/*
 * @Author: licongwei
 * @Date: 2022-02-10 14:53:38
 * @Description: 封装echart绘图类，深合并\转换 配置项
 */
import * as echarts from 'echarts'
import axios from 'axios'
import optionMap from './options/index'
import { merge, conver, deepClone } from './utils'
import { IObj, ICarousel } from './types'
class CreateChart {
  chartInstance!: echarts.ECharts
  chartEl: HTMLElement | null
  map: IObj = { value: '系列' }
  forceRedraw = false
  adcode = ''
  options: IObj = {}
  event: IObj = {
    click: () => {},
    getZrClick: () => {},
    mousemove: () => {},
    mouseover: () => {},
    mouseout: () => {},
    globalout: () => {},
    customEvent: () => {},
    carouselChange: () => {}
  }
  custom = false
  carousel = {
    show: false,
    trigger: 'axis', // 触发类型（axis | item）
    interval: 2000, // 轮播间隔时间
    direction: 0 // 轮播方向（0 | 1）
  }
  private myChart: echarts.ECharts | undefined
  private carouselFlag = true
  private carouselTimer: NodeJS.Timer | null = null
  private _resize: () => void

  constructor(
    chartEl: HTMLElement | null,
    map: IObj,
    forceRedraw: boolean,
    adcode: string,
    options: IObj,
    event: IObj,
    custom: boolean,
    carousel: ICarousel
  ) {
    this.chartEl = chartEl
    // ※ map用于conver方法遍历转换配置项格式
    map && (this.map = map)
    forceRedraw && (this.forceRedraw = forceRedraw)
    adcode && (this.adcode = adcode)
    // 深拷贝options,否则会改变传入的options,导致下一次重绘无法正确conver.
    options && (this.options = deepClone(options))
    event && (this.event = event)
    custom && (this.custom = custom)
    carousel && (this.carousel = carousel)
    this._resize = this.resize.bind(this)
    this.initChart()
  }
  // 绘图
  initChart() {
    if (this.adcode) {
      axios.get(`./geojson/${this.adcode}.json`).then((geoJson) => {
        console.log('geoJson', geoJson)
        echarts.registerMap(this.adcode, {
          geoJSON: geoJson.data,
          specialAreas: {}
        })
        this.drawChart()
      })
    } else {
      this.drawChart()
    }
  }
  drawChart() {
    this.myChart = echarts.getInstanceByDom(this.chartEl as HTMLDivElement)
    if (!this.custom) {
      this.setOptConver()
    }
    // 判断实例是否已存在
    if (!this.myChart) {
      this.chartInstance = echarts.init(
        this.chartEl as HTMLDivElement,
        undefined,
        { renderer: 'svg' }
      )
      this.addEvent()
    } else {
      this.chartInstance = this.myChart
      this.addEvent()
    }
    // 强制销毁实例再重绘
    if (this.forceRedraw && this.myChart) {
      this.dispose()
      this.chartInstance = echarts.init(
        this.chartEl as HTMLDivElement,
        undefined,
        { renderer: 'svg' }
      )
      this.addEvent()
    }
    this.chartInstance?.setOption(this.setOption(), true)
    window.addEventListener('resize', this._resize)
    // !this.myChart && this.addEvent()
    this.setCarousel(this.options.dataset.source.length)
  }
  // 配置项格式转换
  setOptConver() {
    // 待完善：此处只考虑了数组形式（yAxis可能是数组也可能是对象），还需要考虑对象类型；
    // 需要转换格式的配置项(可能还有其他配置，应用过程中如果需要时在此处添加)，先格式转换再合并；
    const converArr = ['yAxis', 'series']
    converArr.forEach((item) => {
      // 分别对全局配置和传入配置(disabled除外)格式转换
      optionMap[item] = conver(optionMap[item], this.map)
      if (!this.options[item]?.disabled) {
        this.options[item] = conver(this.options[item], this.map)
      }
    })
  }
  // 配置项
  setOption(): echarts.EChartsOption {
    const newOpt = this.custom ? this.options : merge(optionMap, this.options)
    // 删除disabled的配置项（有些配置项不适用于所有图表类型）
    for (const item in this.options) {
      if (this.options[item]?.disabled) {
        delete newOpt[item]
      }
    }
    return newOpt
  }
  // 销毁
  dispose() {
    window.removeEventListener('resize', this._resize)
    this.chartInstance && this.chartInstance.dispose()
  }
  // 图表大小自适应
  resize() {
    this.chartInstance && this.chartInstance.resize()
  }
  // 鼠标事件
  addEvent() {
    this.chartInstance.on('click', this.event.click)
    this.chartInstance.getZr().on('click', this.event.getZrClick)
    this.chartInstance.on('mousemove', this.event.mousemove)
    this.chartInstance.on('mouseover', this.event.mouseover)
    this.chartInstance.on('mouseout', this.event.mouseout)
    this.chartInstance.on('globalout', this.event.globalout)
    this.event.customEvent && this.event.customEvent(this.chartInstance)
  }
  // 暂停轮播
  isScroll(bool: boolean) {
    this.carouselFlag = !bool
  }
  // 轮播
  setCarousel(dataLength: number) {
    const { show, trigger, interval, direction } = this.carousel
    if (!this.carousel || !show) return
    // let carouselFlag = true
    let carouselIndex = -1
    // 添加鼠标移入移出事件，需判断图表触发类型
    if (trigger === 'axis') {
      let moveTime = 0
      this.chartInstance.getZr().on('mousemove', (params: any) => {
        if (moveTime && Date.now() - moveTime < 1000) return
        moveTime = Date.now()
        this.carouselFlag = false
        const pointInPixel = [params.offsetX, params.offsetY]
        if (this.chartInstance.containPixel('grid', pointInPixel)) {
          const zrIndex = Math.abs(
            this.chartInstance.convertFromPixel({ seriesIndex: 0 }, [
              params.offsetX,
              params.offsetY
            ])[direction]
          )
          if (zrIndex !== carouselIndex) {
            this.chartInstance.dispatchAction({
              type: 'downplay',
              dataIndex: carouselIndex
            })
          }
        }
      })
      this.chartInstance.on('globalout', () => {
        this.carouselFlag = true
      })
    } else if (trigger === 'item') {
      this.chartInstance.on('mouseover', (params: any) => {
        this.carouselFlag = false
        if (params.dataIndex !== carouselIndex) {
          this.chartInstance.dispatchAction({
            type: 'downplay',
            dataIndex: carouselIndex
          })
        }
      })
      this.chartInstance.on('mouseout', () => {
        this.carouselFlag = true
      })
    }
    this.carouselTimer = setInterval(() => {
      if (this.carouselFlag) {
        // 取消之前高亮的图形
        this.chartInstance.dispatchAction({
          type: 'downplay',
          seriesIndex: 0,
          dataIndex: carouselIndex
        })
        carouselIndex = (carouselIndex + 1) % dataLength
        this.event.carouselChange(carouselIndex)
        // 高亮当前图形
        this.chartInstance.dispatchAction({
          type: 'highlight',
          seriesIndex: 0,
          dataIndex: carouselIndex
        })
        this.chartInstance.dispatchAction({
          type: 'showTip',
          seriesIndex: 0,
          dataIndex: carouselIndex
        })
      }
    }, interval)
  }
  // 清除轮播定时器
  clearInterval() {
    this.carouselTimer && clearInterval(this.carouselTimer)
  }
}
export default CreateChart
